如何优化 RAG 的检索效果?
RAG 效果差,多数先死在检索:该召回的 chunk 没进 top-k,后面生成再强也是「有据错引」。本篇按 索引 → 查询 → 检索 → 评测 讲优化思路与面试答法;概念见 RAG 与检索增强;动手见 RAG 落地实践、[LangChain RAG 实践](/docs/ai/RAG/LangChain RAG 实践);全局/关系题见 [GraphRAG 概览与查询](/docs/ai/RAG/GraphRAG 概览与查询)。
先分清:优化检索 vs 优化生成
| 症状 | 更可能问题 | 优先优化 |
|---|---|---|
| 引用了无关段落 | 检索噪声大、精排不够 | 重排、混合检索、缩 chunk |
| 库里明明有却答不出 | 召回不足 | 增大 k、混合检索、查询改写 |
| 答案跨段被切断 | 切分策略 | 重叠、按结构切、Parent-Document |
| 专有名词/编号搜不到 | 纯向量弱于关键词 | BM25 混合、元数据过滤 |
| 全局归纳题(「整体趋势」) | 向量局部相似度不够 | GraphRAG / 社区摘要 |
面试先一句话:用评测集看是 recall 不够还是 precision 不够,再对症下刀,别一上来堆 rerank。
优化总图
索引侧 查询侧 检索侧
──────── ────── ──────
切分策略 ──┐ 查询改写 ──┐ 混合检索(向量+BM25)
重叠/结构切 ├→ 更好 chunk HyDE/多查询 ├→ 更好 query → 扩大召回 k
Embedding ─┤ 意图识别 ──┘ Rerank 精排
元数据/权限 ─┘ 阈值 + 元数据过滤
Parent-Document 回填
↓
小评测集:Recall@k / MRR / 人工判相关
1. 索引侧:让 chunk「可搜、可定位」
检索上限由索引决定——垃圾进,垃圾出。
| 手段 | 做法 | 解决什么 |
|---|---|---|
| 按结构切分 | 按标题 / 段落 / Markdown 层级,而非固定字数硬切 | 语义不断裂 |
| 重叠(overlap) | 相邻 chunk 共享 10%~20% 文本 | 边界处答案被切断 |
| chunk 大小 | 常见 256~512 token;表格/代码可单独策略 | 太大噪声、太小缺上下文 |
| 元数据 | 来源、章节、产品线、版本、租户、时间 | 检索时过滤、可解释引用 |
| Embedding 选型 | 与语料语言/领域匹配;必要时微调 embedding | 语义空间不对齐 |
| Contextual Retrieval | 索引时给每个 chunk 补「文档级上下文摘要」再 embed | chunk 脱离原文后语义模糊 |
Parent-Document / 小块搜大块读: 索引用小 chunk 提高定位精度,命中后回填父段落或相邻 chunk 给生成——兼顾 recall 与上下文完整。
2. 查询侧:让用户「问法」更接近库里的「写法」
| 手段 | 做法 | 适用 |
|---|---|---|
| 查询改写 | LLM 把口语问题改写成检索友好表述 | 用户问法飘、指代多 |
| Multi-Query | 一次生成多个检索 query,结果合并去重 | 召回面窄 |
| HyDE | 先让 LLM 编「假答案」再 embed 检索 | 问句与文档风格差异大 |
| Step-back | 先问更抽象的上位问题,再检索 | 需要背景概念的问题 |
| 对话上下文 | 多轮时把上一轮意图拼进 query | 「它」「那个政策」缺主语 |
注意:查询改写会增加延迟与成本——PoC 可先做 规则 + 一轮改写,用失败 case 决定是否上 HyDE。
3. 检索侧:先召回,再精排
混合检索(Hybrid Search)
用户 query ─┬─ 向量检索 top-N
└─ BM25 / 关键词 top-N
↓
分数融合(RRF 等)→ 候选集
- 向量:语义相似,「退货怎么弄」≈「如何申请退款」
- BM25:精确 token、SKU、错误码、法条编号
- 工业界常见:RRF(Reciprocal Rank Fusion) 融合两路排序,比简单加权稳
两阶段:召回 + Rerank
第一阶段:混合检索,k 开大(如 20~50)——保 recall
第二阶段:Cross-Encoder Reranker 精排 → top 3~5 ——提 precision
Rerank 模型看 query 与 chunk 的交互,比 bi-encoder 向量相似度准,但更贵——适合「第一阶段噪声大」的场景。
其他检索技巧
| 技巧 | 说明 |
|---|---|
| 相似度阈值 | 低于阈值拒答,防硬凑无关段 |
| 元数据预过滤 | 先按产品/版本/权限筛子集再向量搜 |
| Multi-Vector | 一 chunk 多个 embedding(如摘要 + 原文) |
| HyDE + 混合 | 常与混合检索组合,不是互斥 |
4. 按失败 case 选型(面试加分)
| 失败现象 | 优先尝试 |
|---|---|
| 编号/术语搜不到 | 混合检索 + 元数据 |
| 相关段在 top-20 但不在 top-3 | 加 Rerank |
| 相关段根本不在 top-k | 增大 k、Multi-Query、查 chunk 切分 |
| 跨文档归纳 | GraphRAG Global 查询 / 预建摘要索引 |
| 表格/代码块 | 专用 parser、整表/整段为 chunk |
| 多租户串库 | 检索必带 tenant filter |
答法:先 Naive 向量基线 + 小评测集,按 bad case 加混合 → rerank → 改写 → 结构切分,比一口气背十个缩写更像做过项目。
5. 怎么知道「优化有效」
别凭感觉,建 检索层评测(可与生成层分开):
| 指标 | 含义 |
|---|---|
| Recall@k | 标准答案所在 chunk 是否出现在 top-k |
| MRR | 第一个相关结果排第几 |
| nDCG | 排序质量(相关度分级时) |
| 人工判相关 | 小集上标注「该段能否回答问题」 |
最小实践:20~50 条 (question, gold_chunk_id),改 chunk / k / 混合 / rerank 后看 Recall@5 是否上升——见 RAG 落地实践 的 eval 思路。
生成层指标(答案对不对)会掩盖检索问题;面试可说:检索评测先行,端到端评测后置。
常见坑
| 坑 | 说明 |
|---|---|
| 只调 top-k | k 大 recall 升但噪声进 Prompt,需配合 rerank 或阈值 |
| chunk 一刀切 | PDF/表格/Markdown 同一参数,边界处处漏 |
| 忽略权限 | 检索未过滤 tenant,泄漏比答错更严重 |
| 过度 HyDE | 假答案跑偏会带偏检索 |
| 不重索引 | 改了切分/embedding 策略却沿用旧向量库 |
面试参考答法(60~90 秒)
优化 RAG 检索我会先建小评测集,看是召回不够还是排序太噪。
索引侧:按文档结构切 chunk、加 overlap,补元数据;必要时 Parent-Document——小块检索、大块生成。
查询侧:多轮对话补全指代,必要时 LLM 改写或多 query 扩展。
检索侧:专有名词和编号加 BM25 与向量做混合检索;第一阶段 k 开大保 recall,第二阶段 Cross-Encoder rerank 提 precision;低分阈值则拒答。
全局归纳类再考虑 GraphRAG 或预摘要,别用调 k 硬扛。每改一项用 Recall@k 验证,避免只调生成 Prompt 掩盖检索问题。
延伸阅读
| 篇 | 关系 |
|---|---|
| RAG 与检索增强 | 流水线与失效模式速览 |
| [LangChain RAG 实践](/docs/ai/RAG/LangChain RAG 实践) | Retriever / Reranker 拼装 |
| [Milvus 向量库实践](/docs/ai/RAG/Milvus 向量库实践) | 向量索引与过滤 |
| [GraphRAG 概览与查询](/docs/ai/RAG/GraphRAG 概览与查询) | 全局/关系检索 |
面试速记
- 先诊断: Recall 不足 vs 排序太噪,决定加召回还是加 rerank。
- 索引: 结构切分 + overlap + 元数据;Parent-Document 小块搜大块读。
- 查询: 改写 / Multi-Query / HyDE,解决问法与文档风格 gap。
- 检索: 混合检索(向量+BM25)→ 大 k 召回 → Rerank 精排 → 阈值拒答。
- 评测: Recall@k / MRR 先行;按 bad case 迭代,别只调生成 Prompt。